/*
 Автор: Швейкин Евгений Юрьевич

 Функциональное описание:     
    Модуль содержит фифо, а если в нем есть свободное место, считывает по амм. Если в фифо места
меньше чем на одну пачку данных, чтение останавливается.
    Считывание из фифо ведется на сторонней частоте.
    
    На вход модуля подается:
     - количество данных, которое необходимо считать, в байтах;
     - адрес памяти откуда читать, в Байтах;
     - запускающий импульс.
*/
//--------------------------------------------------------------------------------------------------
// ********************** МАКРОСЫ ******************************************************************

    // Описывает процесс модуля выделения фронтов сигнала. Строб формируется на том же такте.
    // Так же описывает создание новых сигналов с именем <имя сигнала> + <_rise> (<_fall>, <_edge>)
    `define FRONT(clk, name)                            \
        logic name``_0;                                 \
        logic name``_rise;                              \
        logic name``_fall;                              \
        logic name``_edge;                              \
        assign name``_rise = name & (~name``_0);        \
        assign name``_fall = name``_0 & (~name);        \
        assign name``_edge = name ^ name``_0;           \
        always_ff @(posedge clk)                        \
        begin                                           \
            name``_0 <= name;                           \
        end

    // Описывает процесс пересинхронизации для исключения метастабильности
    `define RESYNC(clk, name, bw)                       \
        logic [bw - 1:0] name``_resync_0``clk;          \
        logic [bw - 1:0] name``_``clk;                  \
        always_ff @(posedge clk)                        \
        begin                                           \
            name``_resync_0``clk <= name;               \
            name``_``clk    <= name``_resync_0``clk;    \
        end
// *********************** НАЧАЛО МОДУЛЯ ***********************************************************
module amm_to_fifo_burst_bw
#(  
    parameter MAX_BURST     = 32,       // максимальный размер пачки
    parameter BUF_SZ        = 1024,      // Размер фифо
    parameter AMM_BW        = 32,       // ширина шины авалон
    parameter D_BW          = 32        // ширина шины данных
    
)
(
    input  logic                reset_n,
    
    // Управление модулем
    input  logic                clk_ctrl,    
    input  logic                start_i,
    input  logic [31:0]         data_size_i,                // Слов
    input  logic [31:0]         base_addr_i,                // Байт
    
    output logic                busy_o,

    // Порты фифо
    input  logic                clk_fifo,
    input  logic                fifo_rdreq_i,
    
    output logic [D_BW - 1:0]   fifo_q_o,
    output logic                fifo_rdempty_o,
    output logic [$clog2(BUF_SZ/D_BW):0]    fifo_rdusedw_o,
    
    // Сигналы шины master авалон
    input  logic                clk_amm,
    input logic                 avm_waitrequest,
    input logic [AMM_BW - 1:0]  avm_readdata,
    input logic                 avm_readdatavalid,

    output logic [31:0]         avm_address,
    output logic [9:0]          avm_burstcount,
    output logic                avm_beginbursttransfer,
    output logic                avm_read
);
 
//  ********************* КОНСТАНТЫ ****************************************************************
    localparam FIFO_SZ      = BUF_SZ / AMM_BW * 8;
    localparam FIFO_AW_I    = $clog2(BUF_SZ / AMM_BW * 8);
    localparam FIFO_AW_O    = $clog2(BUF_SZ / D_BW * 8);
    
//  ********************* ФУНКЦИИ ******************************************************************

//  ********************* ТИПЫ ДАННЫХ **************************************************************
    // Состояния автомата считывания пакетов из фифо и передачи их по Avalon
    enum logic [4:0]    {   IDLE_s,
                            AMM_SET_BEGINBURST_s,
                            AMM_SET_READ_s,
                            AMM_RD_BURST_s,
                            CALC_s,
                            WAIT_s
                        } state_amm_to_fifo_burst_bw, next_state;
                        
    typedef struct packed 
    {   
        logic [AMM_BW - 1:0]    data;
        logic                   wrreq;
        logic [FIFO_AW_I:0]     wrusedw;
        logic                   wrfull;
        
        logic [D_BW - 1:0]      q;
        logic                   rdreq;
        logic [FIFO_AW_O:0]     rdusedw;
        logic                   rdempty;
    } fifo_t;
    
// ********************* ПЕРЕМЕННЫЕ ****************************************************************
    fifo_t                          fifo;
    
    logic                           start_latch;
    logic [31:0]                    base_addr_latch;
    logic [31:0]                    data_size_latch;
    
    logic                           soft_reset;  
    logic [9:0]                     burst_word_cnt;
    logic [9:0]                     current_burst;
    
    logic                           end_burst;
    logic                           end_data;
    logic [31:0]                    address;
    
    logic [31:0]                    data_word_cnt;
    
    logic [9:0]                     read_cnt;
    logic                           reset_fifo;
    logic                           busy;
// ********************* ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ ДЛЯ СИМУЛЯТОРА ***********************************

    
//  ********************* ASSIGN *******************************************************************
    assign busy_o           = busy_clk_ctrl;
    assign fifo_q_o         = fifo.q;
    assign fifo.rdreq       = fifo_rdreq_i;
    assign fifo_rdempty_o   = fifo.rdempty;
    assign fifo_rdusedw_o   = fifo.rdusedw;
    
// ********************* ПРОЦЕССЫ ******************************************************************
    `FRONT (clk_ctrl, start_i)
    `FRONT (clk_amm, start_latch_clk_amm)
    
    // Пересинхронизируем для исключения метастабильности
    `RESYNC(clk_amm, data_size_latch, 32)
    `RESYNC(clk_amm, base_addr_latch, 32)
    `RESYNC(clk_amm, busy, 1)
    `RESYNC(clk_amm, start_latch, 1)
    
    `RESYNC(clk_ctrl, busy, 1)
    
    `RESYNC(clk_fifo, busy, 1)
    
    // Защелка стартового, стопового сигналов и патаметров записи
    always_ff @(posedge clk_ctrl)
    begin
        // По фронту входного сигнала запуска защелкиваем параметры записи
        if (start_i_rise && (!busy_clk_ctrl)) begin
            start_latch     <= 1'b1;
            data_size_latch <= data_size_i >> ($clog2(AMM_BW / 8)); // Перевели в слова шириной в шину
            base_addr_latch <= base_addr_i;
        end
        else if (busy_clk_ctrl)
            start_latch <= 1'b0;
        else;
    end 
    
    always_ff @(posedge clk_amm)
    begin
        if (avm_beginbursttransfer)
            read_cnt    <= 10'd0;
        else if (avm_read)
            read_cnt    <= read_cnt + 10'd1;
        else;
    end   
    
   // Переключение состояний автомата (классическое описание)
    always_ff @(posedge clk_amm,  negedge reset_n)
    begin
        if (!reset_n) begin
            state_amm_to_fifo_burst_bw   <= IDLE_s;
        end
        else begin
            state_amm_to_fifo_burst_bw   <= next_state; 
        end
    end
    
    // Определение следующего состояния автомата
    always_comb        
    begin    
        next_state = state_amm_to_fifo_burst_bw; 
        unique case (state_amm_to_fifo_burst_bw)
            IDLE_s:  
            begin
                if (start_latch_clk_amm_rise && (|data_size_latch_clk_amm))
                    next_state  = AMM_SET_BEGINBURST_s;   
                else;
            end     

            AMM_SET_BEGINBURST_s:
            begin                
                // if (!avm_waitrequest)
                    next_state  = AMM_SET_READ_s;
                // else;
            end
            
            AMM_SET_READ_s:
            begin
                if (!avm_waitrequest)
                    next_state  = AMM_RD_BURST_s;
                else;
            end
            
            AMM_RD_BURST_s:
            begin
                if (end_burst) begin
                    if (end_data)
                        next_state  = IDLE_s;
                    else 
                        next_state  = CALC_s;
                end
                else;
            end
            
            CALC_s:
            begin
                next_state  = WAIT_s;
            end
            
            WAIT_s:
            begin
                if (fifo.wrusedw < FIFO_SZ - MAX_BURST)
                    next_state  = AMM_SET_BEGINBURST_s;
                else;
            end
            
            default: ;
        endcase
    end   

    // Счетчик адресов и переключение банков
    always_ff @(posedge clk_amm)
    begin
        unique case (state_amm_to_fifo_burst_bw)
            IDLE_s:
            begin
                if (start_latch_clk_amm_rise && (|data_size_latch_clk_amm)) begin
                    data_word_cnt   <= data_size_latch_clk_amm;
                    address         <= base_addr_latch_clk_amm;
                end
                else;
            end
            
            AMM_SET_BEGINBURST_s:     
            begin
                burst_word_cnt  <= 10'd0;
                end_burst       <= 1'b0;
                current_burst   <= avm_burstcount;

            end

            AMM_RD_BURST_s:     
            begin
                // if (avm_read && (!avm_waitrequest)) begin
                if (avm_readdatavalid) begin
                    end_burst       <= 1'b1;
                    burst_word_cnt  <= burst_word_cnt + 10'd1;
                    if (burst_word_cnt < current_burst - 1) begin
                        end_burst       <= 1'b0;
                    end
                    else;
              
                    end_data        <= 1'b1;
                    data_word_cnt   <= data_word_cnt - 32'd1;
                    if (data_word_cnt > 32'd1) begin
                        end_data        <= 1'b0;
                    end
                    else;
                end
                else;
            end
                                
            CALC_s:
            begin
                address <= address + (burst_word_cnt << ($clog2(AMM_BW / 8)));
            end
            default: ;
        endcase
    end
    
    // Описание выходов автомата 
    always_comb
    begin
        if (data_word_cnt > MAX_BURST)
            avm_burstcount = MAX_BURST[9:0];
        else    
            avm_burstcount = data_word_cnt[9:0];
        // avm_burstcount          = MAX_BURST;
        avm_beginbursttransfer  = 1'b0;
        avm_read                = 1'b0;
        avm_address             = address;
        fifo.wrreq              = 1'b0;
        fifo.data               = {AMM_BW{1'b0}};
        busy                    = 1'b1;
        reset_fifo              = 1'b0;
        unique case (state_amm_to_fifo_burst_bw)
            IDLE_s:  
            begin
                busy    = 1'b0;
                reset_fifo  = start_latch_clk_amm;
            end     
            
            AMM_SET_BEGINBURST_s:     
            begin
                avm_beginbursttransfer  = 1'b1;                 // Запрос
                avm_read                = 1'b1;//~avm_readdatavalid;//1'b1;
            end
            
            AMM_SET_READ_s:     
            begin
                avm_read                = 1'b0;
            end
            
            AMM_RD_BURST_s:     
            begin
                if (avm_readdatavalid) begin
                    fifo.wrreq  = 1'b1;
                    fifo.data   = avm_readdata;
                end
                else;

            end
            default: ;
        endcase
    end
    
// ********************* ПОДКЛЮЧАЕМЫЕ МОДУЛИ *******************************************************
    // Буфер FIFO
    dcfifo_mixed_widths
    #(  
        .add_usedw_msb_bit      ("ON"),             // разрядность usedw на весь диапазон
        .lpm_showahead          ("ON"),             // режим ASK включен
        .lpm_type               ("dcfifo_mixed_widths"),         // Разные частоты входа/выхода
        .overflow_checking      ("ON"),             // Проверка переполнения
        .underflow_checking     ("ON"),             // Проверка опустошения
        .use_eab                ("ON"), 
        .rdsync_delaypipe       (4),                
        .wrsync_delaypipe       (4),
        
        .lpm_numwords           (FIFO_SZ),          // Объем фифо
        
        .lpm_width              (AMM_BW),           // Разрядность входных данных 
        .lpm_widthu             (FIFO_AW_I),        // Разрядность адреса входа
        .lpm_width_r            (D_BW),             // Разрядность выходных данных
        .lpm_widthu_r           (FIFO_AW_O)         // Разрядность адреса выхода
    )
    fifo_inst1
    (   
        .aclr                   (~reset_n | reset_fifo),
    
        // Запись в фифо
        .wrclk                  (clk_amm),    
        .wrreq                  (fifo.wrreq),
        .data                   (fifo.data),
        .wrfull                 (fifo.wrfull),
        .wrusedw                (fifo.wrusedw),
      
        // Чтение из фифо
        .rdclk                  (clk_fifo),
        .rdreq                  (fifo.rdreq),
        .q                      (fifo.q),
        .rdusedw                (fifo.rdusedw),
        .rdempty                (fifo.rdempty)
    );
endmodule 

// Удаляем макросы, чтобы никому не испортить ничего
`undef FRONT
`undef RESYNC